// pulled & edited from http://www.c-sharpcorner.com//Code/2002/Mar/SavingNRestoringWinJM.asp

using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.Win32;

namespace Kailua.net.windward.utils.forms
{
	/// <summary>
	/// Add this class as a member of a Form and it will save it's position and size.
	/// </summary>
	public class PersistWindowState : System.ComponentModel.Component
	{

		/// <summary>
		/// Delegate for saving additional information on load/store.
		/// </summary>
		public delegate void WindowStateDelegate(object sender, RegistryKey key);
		/// <summary>
		/// Called when a window is loaded.
		/// </summary>
		public event WindowStateDelegate LoadStateEvent;
		/// <summary>
		/// Called when a window is closed.
		/// </summary>
		public event WindowStateDelegate SaveStateEvent;

		private readonly Form m_parent;
		private readonly string m_regPath;
		private int m_normalLeft;
		private int m_normalTop;
		private int m_normalWidth;
		private int m_normalHeight;
		private FormWindowState m_windowState;
		private bool m_allowSaveMinimized = false;

		/// <summary>
		/// Create an object to persist the Form position and size.
		/// </summary>
		/// <param name="parent">The Form to be persisted.</param>
		/// <param name="regpath">The registry entry to save under.</param>
		public PersistWindowState(Form parent, string regpath)
		{
			m_regPath = regpath;

			m_parent = parent;

			// subscribe to parent form's events
			m_parent.Closing += OnClosing;
			m_parent.Resize += OnResize;
			m_parent.Move += OnMove;
			m_parent.Load += OnLoad;

			// get initial width and height in case form is never resized
			m_normalWidth = m_parent.Width;
			m_normalHeight = m_parent.Height;
		}

		/// <summary>
		/// The registry saving under.
		/// </summary>
		public string RegistryPath
		{
			get { return m_regPath; }
		}

		/// <summary>
		/// Set to true to allow minimized to be an ok state to save.
		/// </summary>
		public bool AllowSaveMinimized
		{
			set { m_allowSaveMinimized = value; }
		}

		private void OnResize(object sender, EventArgs e)
		{
			// save width and height
			if (m_parent.WindowState == FormWindowState.Normal)
			{
				m_normalWidth = m_parent.Width;
				m_normalHeight = m_parent.Height;
			}
		}

		private void OnMove(object sender, EventArgs e)
		{
			// save position
			if (m_parent.WindowState == FormWindowState.Normal)
			{
				m_normalLeft = m_parent.Left;
				m_normalTop = m_parent.Top;
			}
			// save state
			m_windowState = m_parent.WindowState;
		}

		private void OnClosing(object sender, System.ComponentModel.CancelEventArgs e)
		{

			// save position, size and state
			RegistryKey key = Registry.CurrentUser.CreateSubKey(m_regPath);
			key.SetValue("Left", m_normalLeft);
			key.SetValue("Top", m_normalTop);
			key.SetValue("Width", m_normalWidth);
			key.SetValue("Height", m_normalHeight);

			// check if we are allowed to save the state as minimized (not normally)
			if (!m_allowSaveMinimized)
				if (m_windowState == FormWindowState.Minimized)
					m_windowState = FormWindowState.Normal;

			key.SetValue("WindowState", (int)m_windowState);

			// fire SaveState event
			if (SaveStateEvent != null)
				SaveStateEvent(this, key);
		}

		private void OnLoad(object sender, EventArgs e)
		{
			// attempt to read state from registry
			RegistryKey key = Registry.CurrentUser.OpenSubKey(m_regPath);
			if (key != null)
			{
				int left = (int)key.GetValue("Left", m_parent.Left);
				int top = (int)key.GetValue("Top", m_parent.Top);
				int width = (int)key.GetValue("Width", m_parent.Width);
				int height = (int)key.GetValue("Height", m_parent.Height);
				FormWindowState windowState = (FormWindowState)key.GetValue("WindowState", (int)m_parent.WindowState);

				// keep it visible - find it's monitor and make it fully in the monitor
				Screen[] monitors = Screen.AllScreens;
				Screen monitorIn = Screen.PrimaryScreen;
				foreach (Screen screen in monitors)
				{
					if (screen.Bounds.Contains(left, top))
					{
						monitorIn = screen;
						break;
					}
				}
				left = Math.Min(left, monitorIn.Bounds.Right - width);
				left = Math.Max(left, monitorIn.Bounds.Left);
				top = Math.Min(top, monitorIn.Bounds.Bottom - height);
				top = Math.Max(top, monitorIn.Bounds.Top);

				m_parent.Location = new Point(left, top);
				m_parent.Size = new Size(width, height);
				m_parent.WindowState = windowState;

				// fire LoadState event
				if (LoadStateEvent != null)
					LoadStateEvent(this, key);
			}
		}
	}
}